package org.javaongems.client.forms;

import org.javaongems.client.Gem;
import org.javaongems.client.rt.awt.Rectangle;
import org.javaongems.client.rt.awt.ScreenRectUtils;
import org.javaongems.client.rt.text.dom.DomExtUtils;
import org.javaongems.client.vcl.Container;
import org.javaongems.client.vcl.Control;
import org.javaongems.client.vcl.ControlDomRenderer;
import org.javaongems.client.vcl.ControlEvent;
import org.javaongems.client.vcl.FormTag;
import org.javaongems.client.vcl.OpaqueSelection;
import org.javaongems.client.vcl.WinControl;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;

/**
 * @author Ashin Wimalajeewa (ash)
 * 
 * TODO: need to work out how to set the active control
 */
public class CustomForm extends Container implements FormTag {
	final static public int BI_MINIMISE = 1;
	final static public int BI_MAXIMISE = 1 << 1;
	
	final static public int BS_DIALOG = 0;	// no min, no max, no resize
	final static public int BS_SINGLE = 1;	// min, max, no resize
	final static public int BS_NONE = 2;
	final static public int BS_SIZEABLE = 3;	// configurable min, max
	final static public int BS_TOOLWINDOW = 4;	// no min, no max, no resize (small title)
	final static public int BS_SIZETOOLWIN = 5;	// no min, no max (small title)

	final static public int FS_NORMAL = 0;
	final static public int FS_MDICHILD = 1;
	final static public int FS_MDIFORM = 2;
	final static public int FS_STAYONTOP = 3;

	final static public int DEFAULT_BORDER_ICONS = BI_MINIMISE | BI_MAXIMISE;
	
	protected Control activeControl;
	protected int borderIcons = DEFAULT_BORDER_ICONS;
	protected int borderStyle = BS_SIZEABLE;
	protected int formStyle = FS_NORMAL;
	
	protected int buttonHeight = 19;
	protected int titleBarHeight = 22;
	protected boolean frameStrutsAttached;
	
	protected Element titleBarElement, captionElement, borderIcnsElement;
	protected Element minBtnElement, maxBtnElement, closeBtnElement;
//	protected Element minDivElement, maxDivElement, closeDivElement;
	protected Element resizeHndlDivElement;
	protected Element[] biBtns;
	
	protected OpaqueSelection opaqueSelection = new OpaqueSelection();
	
	public CustomForm() {
		buildFrame();
		if (!(listener instanceof CustomFormListener)) 
			return;
		((CustomFormListener) listener).onCreate(this);
	}
	
	// override
	protected void initialiseClientBounds() {
		super.initialiseClientBounds();
//		clientRect.setBounds(boundsRect);
		int frameMod = (borderStyle == BS_NONE) ? 0: titleBarHeight;
		ScreenRectUtils.reduceAndMove(0, frameMod, clientRect);
	}
	
	public void paintAttributeCaption() {
		if (!isReadyToPaint())
			return;
		decorateCaption();
	}
	
	protected void paintAttributeText() {
		// custom forms do not use text, they only use caption
	}

	// Custom form
	
	public void close() {
		// TODO
	}
	
	public boolean closeQuery() {
		return true;	// TODO 
	}
	
	public void focusControl(WinControl ctrl) {
		setFocusControl(ctrl);
	}
	
	public void hide() {
		setVisible(false);
	}
	
	public boolean isShortCut() {
		return false;	// TODO
	}
	
	public void setFocus() {
		super.setFocus();
	}
	
	public void setFocusControl(WinControl ctrl) {
		ctrl.setFocus();
	}
	
	public void show() {
		setVisible(true);
		bringToFront();
	}
	
	public void showModal() {
		// TODO
	}
	
	// published
	public boolean isAction() {
		return false;
	}
	
	public Control getActiveControl() {
		return activeControl;
	}

	public void setBorderIcons(int bi) {
		if (borderIcons == bi)
			return;
		borderIcons = bi;
		paintBorderIcons();
	}
	
	public int getBorderIcons() {
		return borderIcons;
	}
	
	public void setBorderStyle(int bs) {
		if (borderStyle == bs)
			return;
		borderStyle = bs;
		buttonHeight = isToolWindow() ? 15: 19; 
		recalcClientRectAndUpdateClientChild();
		paintFrameStruts();
		if (borderStyle != BS_NONE) {
			paintTitleBar();
			paintBorderIcons();
			paintFrameResizeHandle();
		}
	}
	
	public int getBorderStyle() {
		return borderStyle;
	}
	
	public void setFormStyle(int fs) {
		if (formStyle == fs)
			return;
		formStyle = fs;
	}
	
	public int getFormStyle() {
		return formStyle;
	}

	// internals
	protected boolean onInternelControlEvent(Element elm) {
		if (super.onInternelControlEvent(elm))
			return true;
		boolean handled = false;
		switch (ControlEvent.eventType) {
	      case Event.ONCLICK: 		handled = handleClick(elm); break;
		}
		return handled;
	}
	
	protected boolean handleClick(Element elm) {
		if (minBtnElement.equals(elm))
			System.out.println("min button pressed");
		else if (maxBtnElement.equals(elm))
			System.out.println("max button pressed");
		else if (closeBtnElement.equals(elm))
			System.out.println("close button pressed");
		else if (resizeHndlDivElement.equals(elm))
			System.out.println("resize button pressed");
		return false;
	}
	
	protected boolean isToolWindow() {
		return borderStyle == BS_SIZETOOLWIN || borderStyle == BS_TOOLWINDOW;
	}
	
	protected boolean isResizable() {
		return borderStyle == BS_SIZEABLE || borderStyle == BS_SIZETOOLWIN;
	}
	
	protected boolean isBorderIconAdjustableInStyle() {
		return borderStyle == BS_SIZEABLE || borderStyle == BS_SINGLE;
	}
	
	protected boolean hasBorderIconsMask(int mask) {
		return ((borderIcons & mask) != 0);
	}
	
	protected void paintFrameStruts() {
		if (!isReadyToPaint())
			return;
		if (borderStyle == BS_NONE) {
			if (frameStrutsAttached)
				detachFrameStruts();
		} else{
			domRenderer.setBackgroundColor("buttonface");
			domRenderer.setBevel(true);
			if (!frameStrutsAttached)
				attachFrameStruts();
		}
	}
	
	protected void attachFrameStruts() {
		DOM.appendChild(domRenderer.element, titleBarElement);
		DOM.appendChild(domRenderer.element, resizeHndlDivElement);
		frameStrutsAttached = true;
	}
	
	protected void detachFrameStruts() {
		if (DOM.getParent(titleBarElement) != null)
			DOM.removeChild(domRenderer.element, titleBarElement);
		if (DOM.getParent(resizeHndlDivElement) != null)
			DOM.removeChild(domRenderer.element, resizeHndlDivElement);
		frameStrutsAttached = false;
	}
	
	protected void paintTitleBar() {
		if (!isReadyToPaint())
			return;
		boolean tool = isToolWindow();
		titleBarHeight = tool ? 16 : 22;
		int captionFontSize = tool ? 12 : 16;
		DOM.setStyleAttribute(titleBarElement, "height", titleBarHeight + "px");
		DOM.setStyleAttribute(captionElement, "fontSize", captionFontSize + "px");
	}
	
	protected void paintBorderIcons() {
		if (!isReadyToPaint())
			return;
		boolean enable = isBorderIconAdjustableInStyle() && hasBorderIconsMask(BI_MINIMISE);
		DOM.setBooleanAttribute(minBtnElement, "disabled", !enable);
		enable = isBorderIconAdjustableInStyle() && hasBorderIconsMask(BI_MAXIMISE);
		DOM.setBooleanAttribute(maxBtnElement, "disabled", !enable);
		enable = borderStyle != BS_NONE;
		DOM.setBooleanAttribute(closeBtnElement, "disabled", !enable);
		boolean tool = isToolWindow();
		int val = titleBarHeight - buttonHeight;
		int top = (val / 2) + 1;
		DOM.setStyleAttribute(borderIcnsElement, "margin", top + "px");

		for (int i = 0; i < biBtns.length; i++) {
			String dimAsStr = buttonHeight + "px";
			DOM.setStyleAttribute(biBtns[i], "width", dimAsStr);
			DOM.setStyleAttribute(biBtns[i], "height", dimAsStr);
		}
	}
	
	protected void paintFrameResizeHandle() {
		if (!isReadyToPaint())
			return;
		boolean enable = isResizable();
		ControlDomRenderer.setVisible(resizeHndlDivElement, enable);
	}

	protected void buildFrame() {
		buildFrameStruts();
		decorateFrameStruts();
	}

	protected void buildFrameStruts() {
		titleBarElement = DOM.createDiv();
		ControlDomRenderer.setMargin(titleBarElement, 2);
		captionElement = DomExtUtils.createAndAppendElement(titleBarElement, "div");
		ControlDomRenderer.setGemsControl(this, captionElement);
		borderIcnsElement = DomExtUtils.createAndAppendElement(titleBarElement, "div");
		minBtnElement = DomExtUtils.createAndAppendElement(borderIcnsElement, "button");
//		minDivElement = DomExtUtils.createAndAppendElement(minBtnElement, "div");
		maxBtnElement = DomExtUtils.createAndAppendElement(borderIcnsElement, "button");
		closeBtnElement = DomExtUtils.createAndAppendElement(borderIcnsElement, "button");
		resizeHndlDivElement = DOM.createDiv();
		ControlDomRenderer.setGemsControl(this, resizeHndlDivElement);
		biBtns = new Element[] {minBtnElement, maxBtnElement, closeBtnElement};
		
		String pubImgs = Gem.stone.getApplImageCtx();
		ControlDomRenderer.setBackgroundImage(minBtnElement, pubImgs + "min_view.gif");
		ControlDomRenderer.setBackgroundImage(maxBtnElement, pubImgs + "max_view.gif");
		ControlDomRenderer.setBackgroundImage(closeBtnElement, pubImgs + "close_view.gif");
	}
	
	protected void decorateFrameStruts() {
		decorateTitleBar();
		decorateCaption();
		decorateBorderIcons();
		decorateBorderIconsButtons();
		decorateResizeHandle();
	}
	
	protected void decorateTitleBar() {
		DOM.setStyleAttribute(titleBarElement, "overflow", "hidden");
		DOM.setStyleAttribute(titleBarElement, "color", "white");
		DOM.setStyleAttribute(titleBarElement, "backgroundColor", "#7596c6");
		DOM.setStyleAttribute(titleBarElement, "width", "100%");
		DOM.setStyleAttribute(titleBarElement, "height", "22px");
	}
	
	protected void decorateCaption() {
		DOM.setStyleAttribute(captionElement, "position", "relative");
		DOM.setStyleAttribute(captionElement, "float", "left");
		DOM.setStyleAttribute(captionElement, "left", "0px");
		DOM.setStyleAttribute(captionElement, "fontSize", "16px");
		DOM.setStyleAttribute(captionElement, "margin", "2px");
		ControlDomRenderer.setColor(captionElement, "#ffffff");
		ControlDomRenderer.setInnerText(captionElement, this.getCaption());
	}

	protected void decorateBorderIcons() {
		int val = titleBarHeight - buttonHeight;
		int top = (val / 2) + 1;
		DOM.setStyleAttribute(borderIcnsElement, "margin", top + "px");

		DOM.setStyleAttribute(borderIcnsElement, "position", "absolute");
		DOM.setStyleAttribute(borderIcnsElement, "top", "0px");
		DOM.setStyleAttribute(borderIcnsElement, "right", "0px");
	}

	protected void decorateBorderIconsButtons() {
		for (int i = 0; i < biBtns.length; i++) {
			DOM.setStyleAttribute(biBtns[i], "float", "left");
			DOM.setStyleAttribute(biBtns[i], "width", buttonHeight + "px");
			DOM.setStyleAttribute(biBtns[i], "height", buttonHeight + "px");	// 20px
			DOM.setEventListener(biBtns[i], this);
		}
	}
	
	protected void decorateResizeHandle() {
		DOM.setStyleAttribute(resizeHndlDivElement, "position", "absolute");
		DOM.setStyleAttribute(resizeHndlDivElement, "float", "right");
		DOM.setStyleAttribute(resizeHndlDivElement, "right", "0px");
		DOM.setStyleAttribute(resizeHndlDivElement, "bottom", "0px");
		DOM.setStyleAttribute(resizeHndlDivElement, "width", "10px");	// new
		DOM.setStyleAttribute(resizeHndlDivElement, "height", "10px");	// 15
		DOM.setStyleAttribute(resizeHndlDivElement, "cursor", "se-resize");
		ControlDomRenderer.setZindex(resizeHndlDivElement, 999);
//		String pubImgs = Gem.stone.getApplImageCtx();
//		ControlDomRenderer.setBackgroundImage(resizeHndlDivElement, pubImgs + "gems-Form-resize.gif");

	}
	
	// drag and drop support for form is move and resizing the form
	protected void doStartDrag() {
		if (borderStyle == BS_NONE)
			return;
		paintMoveResizeBorder();
		DOM.eventCancelBubble(ControlEvent.controlEvent, true);
		DOM.eventPreventDefault(ControlEvent.controlEvent);
	}
	
	protected void paintMoveResizeBorder() {
		if (!opaqueSelection.isAttached()) {
			opaqueSelection.moving = captionElement.equals(ControlEvent.dragSourceElement);
			opaqueSelection.resizing = resizeHndlDivElement.equals(ControlEvent.dragSourceElement);
			Control ownerCtrl = (Control) getOwner();
			Element ownerElem = ownerCtrl.getElement();
			opaqueSelection.attach(ownerElem);
			OpaqueSelection.Selection sel = (OpaqueSelection.Selection) opaqueSelection.map().get(this);
			Rectangle r = getBoundsRect();
			if (sel == null)
				opaqueSelection.add(this, r);
			else
				sel.bounds.setBounds(r);
			opaqueSelection.joinAll();
		}
		int x = ControlEvent.listenerInfo[ControlEvent.LI_CLIENTX];
		int y = ControlEvent.listenerInfo[ControlEvent.LI_CLIENTY];
		opaqueSelection.reset(x, y);
	}
	
	protected void doDragOver(int dragState) {
		super.doDragOver(dragState);
		paintDragMoveResizeBorder();
		DOM.eventCancelBubble(ControlEvent.controlEvent, true);
		DOM.eventPreventDefault(ControlEvent.controlEvent);
	}
	
	protected void paintDragMoveResizeBorder() {
		int x = ControlEvent.listenerInfo[ControlEvent.LI_CLIENTX];
		int y = ControlEvent.listenerInfo[ControlEvent.LI_CLIENTY];
		if (opaqueSelection.moving) {
			opaqueSelection.move(x, y);
		} else if (opaqueSelection.resizing) {
			opaqueSelection.resize(x, y);
		}
	}
	
	protected void doEndDrag() {
		super.doEndDrag();
		opaqueSelection.detach();
		if (opaqueSelection.hadActivity()) {
			OpaqueSelection.Selection sel = (OpaqueSelection.Selection) opaqueSelection.map().get(this);
			setBoundsRect(sel.bounds);			
		}
		DOM.eventCancelBubble(ControlEvent.controlEvent, true);
		DOM.eventPreventDefault(ControlEvent.controlEvent);
	}
}
